#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (C) 1994  Ling Li
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


import gobject, gtk, gtk.gdk
from gtk import *

import datetime, string, logging, time, thread, threading

from coralftp_globals import *
from ftp_engine import FTPEngine
from transfer_view import *
from quick_connect import QuickConnect
from general_input import *
from site_info import *
from ftp_exception import *
from configuration import config_value
from utils import *

class RemoteView:

    COL_ICON = 0
    COL_NAME = 1
    COL_TYPE = 2
    COL_SIZE = 3
    COL_DATE = 4
    COL_ATTR = 5
    COL_OWNER = 6
    COL_GROUP = 7
    COL_INFO = 8

    __ftp_engine = None
    __site_info = None
    __selected_count = 0
    __change_selection_now = False

    # This event object is used when ftp_close() method is called before
    # open a new site.
    __ftp_close_event = threading.Event()

    __clipboard = None

    __change_sort_menu_item = True

    # file listing font
    __use_custom_file_listing_font = False
    __file_listing_font = None
    __text_renderers = []
    __show_hidden = True

    # show idle time
    __show_idle_time = True

    # registered cd-succeeded handler id
    __cd_succeeded_hids = []

    def __init__(self, main_window):

        ACTIONS = {
            'connect' : { 'label' : 'Connect',
                          'update' : self.on_connect_action_update,
                          'execute' : self.on_connect_action_execute },
            'disconnect' : { 'label' : 'Disconnect',
                             'sensitive' : False,
                             'update' : self.on_disconnect_action_update,
                             'execute' : self.on_disconnect_action_execute },
            'refresh' : { 'label' : 'Refresh',
                          'sensitive' : False,
                          'update' : self.on_refresh_action_update,
                          'execute' : self.on_refresh_action_execute },
            'abort' : { 'label' : 'Abort',
                        'sensitive' : True,
                        'execute' : self.on_abort_action_execute },
            'transfer' : { 'label' : '_Transfer',
                           'sensitive' : False,
                           'update' : self.on_transfer_queue_action_update,
                           'execute' : self.on_transfer_action_execute },
            'transfer_as' : {'label' : 'Transfer As',
                             'sensitive' : False,
                             'update' : self.on_transfer_queue_as_action_update,
                             'execute' : self.on_transfer_as_action_execute},
            'queue' : { 'label' : '_Queue',
                        'sensitive' : False,
                        'update' : self.on_transfer_queue_action_update,
                        'execute' : self.on_queue_action_execute },
            'queue_as' : { 'label' : 'Queue As',
                           'sensitive' : False,
                           'update' : self.on_transfer_queue_as_action_update,
                           'execute' : self.on_queue_as_action_execute },
            'delete' : { 'label' : '_Delete',
                         'sensitive' : False,
                         'update' : self.on_delete_action_update,
                         'execute' : self.on_delete_action_execute },
            'move' : { 'label' : '_Move',
                       'sensitive' : False,
                       'update' : self.on_move_action_update,
                       'execute' : self.on_move_action_execute },
            'rename' : { 'label' : 'Re_name',
                         'sensitive' : False,
                         'update' : self.on_rename_action_update,
                         'execute' : self.on_rename_action_execute },
            'copyurl' : { 'label' : 'Copy URL',
                          'sensitive' : False,
                          'update' : self.on_copyurl_action_update,
                          'execute' : self.on_copyurl_action_execute },
            'mkdir' : { 'label' : 'Make Folder',
                        'sensitive' : False,
                        'update' : self.on_mkdir_action_update,
                        'execute' : self.on_mkdir_action_execute },
            'chdir' : { 'label' : 'Change Folder',
                        'sensitive' : False,
                        'update' : self.on_chdir_action_update,
                        'execute' : self.on_chdir_action_execute },
            'updir' : { 'sensitive' : False,
                        'update' : self.on_updir_action_update,
                        'execute' : self.on_updir_action_execute },
            'sort' : { 'sensitive' : True,
                       'execute' : self.on_sort_action_execute },
            'attributes' : { 'sensitive' : False },
            'save_path' : { 'sensitive' : False },
            'show_hidden' : { 'sensitive' : True,
                              'execute' : self.on_show_hidden_action_execute }
            }

        WIDGET_ACTIONS = { 'tbtn_remote_connect' : 'connect',
                           'tbtn_remote_disconnect' : 'disconnect',
                           'tbtn_remote_abort' : 'abort',
                           'tbtn_remote_transfer' : 'transfer',
                           'tbtn_remote_refresh' : 'refresh',
                           'btn_remote_updir' : 'updir' }
        
        MENU_ACTIONS = { 'mi_remote_transfer' : 'transfer',
                         'mi_remote_queue' : 'queue',
                         'mi_remote_transfer_as' : 'transfer_as',
                         'mi_remote_queue_as' : 'queue_as',
                         'mi_remote_delete' : 'delete',
                         'mi_remote_rename' : 'rename',
                         'mi_remote_move' : 'move',
                         'mi_remote_copy_url' : 'copyurl',
                         'mi_remote_attributes' : 'attributes',
                         'mi_remote_save_path' : 'save_path',
                         'mi_remote_make_folder' : 'mkdir',
                         'mi_remote_change_folder' : 'chdir',
                         'mi_remote_refresh' : 'refresh',
                         'mi_remote_arr_by_name' : 'sort',
                         'mi_remote_arr_by_size' : 'sort',
                         'mi_remote_arr_by_date' : 'sort',
                         'mi_remote_arr_by_file_extension' : 'sort',
                         'mi_remote_arr_by_owner' : 'sort',
                         'mi_remote_arr_by_group' : 'sort',
                         'mi_remote_arr_ascending' : 'sort',
                         'mi_remote_arr_descending' : 'sort',
                         'mi_remote_show_hidden' : 'show_hidden' }

        self.__main_window = main_window
        self.__config = main_window.config
        self.__xml = main_window.xml
        self.__menu_xml = get_glade_xml('remote_file_list_menu')
        self.widget = self.__xml.get_widget('remote_view')
        self.widget.connect('destroy', self.on_destroy)
        self.__cmb_dir = self.__xml.get_widget('cmb_remote_dir')
        self.__cmb_dir.child.connect('activate',
                                     self.on_remote_dir_entry_activate)

        # init actions
        self.actions = ActionList(ACTIONS)

        self.__model = ListStore(gtk.gdk.Pixbuf,         # icon
                                 gobject.TYPE_STRING,    # name
                                 gobject.TYPE_STRING,    # type
                                 gobject.TYPE_STRING,    # size
                                 gobject.TYPE_STRING,    # date
                                 gobject.TYPE_STRING,    # attr
                                 gobject.TYPE_STRING,    # owner
                                 gobject.TYPE_STRING,    # group
                                 gobject.TYPE_PYOBJECT)  # info
        self.__model.set_sort_func(RemoteView.COL_NAME, self.filelist_sort)
        self.__model.set_sort_func(RemoteView.COL_SIZE, self.filelist_sort)
        self.__model.set_sort_func(RemoteView.COL_TYPE, self.filelist_sort)
        self.__model.set_sort_func(RemoteView.COL_DATE, self.filelist_sort)
        self.__model.set_sort_func(RemoteView.COL_OWNER, self.filelist_sort)
        self.__model.set_sort_func(RemoteView.COL_GROUP, self.filelist_sort)
        self.__list = self.__xml.get_widget('tv_remote_file_list')
        self.__list.set_model(self.__model)
        self.__list.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
        self.__list.get_selection().set_select_function(self.__select_func)
        
        column = TreeViewColumn(_("Name"))
        renderer = CellRendererPixbuf()
        column.pack_start(renderer, expand=False)
        column.add_attribute(renderer, 'pixbuf', RemoteView.COL_ICON)
        renderer = CellRendererText()
        self.__text_renderers.append(renderer)
        column.pack_start(renderer, expand=True)
        column.add_attribute(renderer, 'text', RemoteView.COL_NAME)
        column.set_sort_column_id(RemoteView.COL_NAME)
        column.set_resizable(True)
        self.__list.append_column(column)
        
        renderer = CellRendererText()
        self.__text_renderers.append(renderer)
        renderer.set_property('xalign', 1)
        column = TreeViewColumn(_("Size"), renderer, text=RemoteView.COL_SIZE)
        column.set_sort_column_id(RemoteView.COL_SIZE)
        column.set_resizable(True)
        self.__list.append_column(column)
        
        renderer = CellRendererText()
        self.__text_renderers.append(renderer)
        column = TreeViewColumn(_("Attrib"), renderer, text=RemoteView.COL_ATTR)
        column.set_resizable(True)
        self.__list.append_column(column)

        renderer = CellRendererText()
        self.__text_renderers.append(renderer)
        column = TreeViewColumn(_("Owner"), renderer, text=RemoteView.COL_OWNER)
        column.set_sort_column_id(RemoteView.COL_OWNER)
        column.set_resizable(True)
        self.__list.append_column(column)

        renderer = CellRendererText()
        self.__text_renderers.append(renderer)
        column = TreeViewColumn(_("Group"), renderer, text=RemoteView.COL_GROUP)
        column.set_sort_column_id(RemoteView.COL_GROUP)
        column.set_resizable(True)
        self.__list.append_column(column)

        renderer = CellRendererText()
        self.__text_renderers.append(renderer)
        column = TreeViewColumn(_("Modified"), renderer,
                                text=RemoteView.COL_DATE)
        column.set_sort_column_id(RemoteView.COL_DATE)
        column.set_resizable(True)
        self.__list.append_column(column)

        # set file list sort by according to gconf 
        self.__model.connect('sort-column-changed',
                             self.on_list_sort_column_changed)
        value = config_value(self.__config, 'display', 'ftp_sort_by')
        if value == 0:
            ftp_sort_by = RemoteView.COL_NAME
        elif value == 1:
            ftp_sort_by = RemoteView.COL_OWNER
        elif value == 2:
            ftp_sort_by = RemoteView.COL_SIZE
        elif value == 3:
            ftp_sort_by = RemoteView.COL_GROUP
        elif value == 4:
            ftp_sort_by = RemoteView.COL_DATE
        elif value == 5:
            ftp_sort_by = RemoteView.COL_TYPE
        else:
            raise ValueError
        value = config_value(self.__config, 'display', 'ftp_order')
        ftp_order = value
        self.__model.set_sort_column_id(ftp_sort_by, ftp_order)

        # add config change listener
        self.__config.notify_add('display', 'display_file_size_as',
                                 self.on_display_file_size_as_change)

        # setup fonts
        self.__use_custom_file_listing_font = config_value(
            self.__config, 'display', 'use_custom_file_listing_font')
        self.__file_listing_font = config_value(
            self.__config, 'display', 'file_listing_font')
        self.__change_font()
        self.__config.notify_add('display', 'use_custom_file_listing_font',
                                 self.on_use_custom_file_listing_font_change)
        self.__config.notify_add('display', 'file_listing_font',
                                 self.on_file_listing_font_change)

        # setup column resize
        self.__config.notify_add('display', 'auto_size_file_list_columns',
                                 self.on_auto_size_file_list_columns_change)
        auto_size_file_list_columns = config_value(
            self.__config, 'display', 'auto_size_file_list_columns')
        for column in self.__list.get_columns():
            if auto_size_file_list_columns:
                column.set_sizing(TREE_VIEW_COLUMN_AUTOSIZE)
            else:
                column.set_sizing(TREE_VIEW_COLUMN_GROW_ONLY)

        # setup show idle time
        self.__show_idle_time = config_value(self.__config, 'display',
                                             'show_site_idle_time')
        self.__config.notify_add('display', 'show_site_idle_time',
                                 self.on_show_site_idle_time_change)

        # rules hint
        self.__list.set_property('rules-hint',
                                 config_value(self.__config, 'display',
                                              'rules_hint'))
        self.__config.notify_add(
            'display', 'rules_hint',
            lambda config, id, value, *args: \
            self.__list.set_property('rules-hint', value.get_bool()))
        
        self.__frame4 = self.__xml.get_widget('frame_remote_4')
        self.__frame5 = self.__xml.get_widget('frame_remote_5')
        self.active_indicator = self.__xml.get_widget('eb_remote')
        self.__status1 = self.__xml.get_widget('label_remote_status1')
        self.__status2 = self.__xml.get_widget('label_remote_status2')

        # connect widget with action
        for widget_name, action_name in WIDGET_ACTIONS.items():
            widget = self.__xml.get_widget(widget_name)
            self.actions[action_name].attach(widget)

        self.__menu = self.__menu_xml.get_widget('remote_file_list_menu')
        for widget_name, action_name in MENU_ACTIONS.items():
            widget = self.__menu_xml.get_widget(widget_name)
            self.actions[action_name].attach(widget)
            
        self.__list.connect('button-release-event',
                            self.on_list_button_release)
        self.__list.connect('button-press-event',
                            self.on_list_button_press)

    def __getattr__(self, name):
        if name == 'model':
            return self.__model
        elif name == 'ftp_engine':
            return self.__ftp_engine
        elif name == 'main_window':
            return self.__main_window
        elif name == 'status1':
            return self.__status1
        elif name == 'status2':
            return self.__status2
        elif name == 'current_path':
            return self.__cmb_dir.child.get_text()
        elif name == 'site_info':
            return self.__site_info
        elif name == 'cmb_dir':
            return self.__cmb_dir
        elif name == 'selected_count':
            return self.__selected_count
        elif name == 'list_updater':
            return ListUpdater(self)
        elif name == 'pwd_updater':
            return PwdUpdater(self)
        elif name == 'config':
            return self.__config
        elif name == 'show_hidden':
            return self.__show_hidden
        elif name == 'current_url':
            url = 'ftp://'
            if self.site_info['username'] != '':
                url = url + self.site_info['username'] + '@'
            url = url + self.site_info['server_addr']
            if self.site_info['server_port'] != '21':
                url = '%s:%d' % (url, int(self.site_info['server_port']))
            url = url + self.current_path
            return url
        else:
            raise AttributeError, name

    def is_ready(self):
        return self.__ftp_engine != None and self.__ftp_engine.status == STATUS_IDLE

    def on_destroy(self, obj):
        if self.__ftp_engine:
            self.__ftp_engine.quit()
        return

    def update_status1(self):
        if not self.__ftp_engine or self.__ftp_engine.status <= STATUS_CONNECTING:
            return
        display_file_size_as = config_value(self.__config, 'display',
                                            'display_file_size_as')
        
        if self.folder_count > 1:
            text = _("%d Folders") % self.folder_count
        else:
            text = _("%d Folder") % self.folder_count
        if self.file_count > 1:
            text = _("%s, %d Files") % (text, self.file_count)
        else:
            text = _("%s, %d File") % (text, self.file_count)
        total = self.folder_count + self.file_count
        text = _("%s, %d Total") % (text, total)
        if self.__selected_count != 0:
            size = 0
            for iter in self.__selections:
                info = self.__model.get_value(iter, RemoteView.COL_INFO)
                if not info.isdir():
                    size = size + info.size
            size = format_size(display_file_size_as, size)
            if size[-1] != 'B': size = size + _(" bytes")
            text = text + _(", %d Selected(%s)") % (self.__selected_count, size)
        else:
            iter = self.__model.get_iter_first()
            size = 0
            while iter:
                info = self.__model.get_value(iter, RemoteView.COL_INFO)
                if not info.isdir():
                    size = size + info.size
                iter = self.__model.iter_next(iter)
            size = format_size(display_file_size_as, size)
            text = text + '(%s)' % (size)
        self.__status1.set_text(text)
        return
    
    def connect_site(self, s = None):
        if s:
            self.__site_info = s
        self.ftp_open()
        return

    def on_connect_action_update(self, obj):
        obj.set_property('sensitive', self.__ftp_engine == None)
        return
        
    def on_connect_action_execute(self, action, widget, *args):
        button_window = widget.get_parent_window()
        x, y = button_window.get_origin()
        w, h = button_window.get_size()

        # Create menu widget
        menu = Menu()

        # Add reconnect item
        if self.__site_info:
            item = MenuItem(_("Reconnect %s") % self.__site_info['site_name'])
            item.connect('activate', lambda *args: self.connect_site())
            menu.append(item)
            menu.append(SeparatorMenuItem())
            
        # Add quick connect item
        item = MenuItem(_("Quick Connect"))
        item.connect('activate', self.on_quick_connect_action_execute)
        menu.append(item)
        menu.append(SeparatorMenuItem())

        # Add items in site manager
        sm = self.__main_window.coralftp.get_site_manager()
        folder = sm.find_folder_by_path('/Sites')
        q = []
        q.append((menu, folder))
        while len(q) > 0:
            m, f = q.pop()
            for name, item in f.items():
                menuitem = MenuItem(name)
                m.append(menuitem)
                if isinstance(item, SiteFolder):
                    nm = Menu()
                    menuitem.set_submenu(nm)
                    q.insert(0, (nm, item))
                elif isinstance(item, SiteInfo):
                    menuitem.connect('activate',
                                     lambda mi, i: self.connect_site(i),
                                     item)
            
        # Show menu
        menu.show_all()
        menu.popup(None, None, lambda menu: (x, y + h, True), 0, 0)
        return

    def on_quick_connect_action_execute(self, widget, *args):
        def on_dialog_response(dlg, resp, *args):
            if resp == 0:
                s = dlg.site_info
            dlg.destroy()
            if resp == 0:
                self.connect_site(s)
            return
        qconn = QuickConnect(self)
        qconn.connect('response', on_dialog_response)
        resp = qconn.run()
        return

    def on_disconnect_action_update(self, obj):
        obj.set_property('sensitive', self.__ftp_engine != None)
        return

    def on_disconnect_action_execute(self, obj, *args):
        self.ftp_close()
        return

    def on_refresh_action_update(self, obj):
        obj.set_property('sensitive', self.__ftp_engine != None and self.__ftp_engine.is_idle)
        return

    def on_refresh_action_execute(self, obj, *args):
        self.ftp_refresh(True)
        return

    def on_abort_action_execute(self, obj, *args):
        if self.__ftp_engine:
            self.__ftp_engine.abort()
        return

    def on_transfer_queue_action_update(self, obj):
        obj.set_property('sensitive', self.__ftp_engine != None and self.__selected_count > 0)
        return

    def on_transfer_queue_as_action_update(self, obj):
        obj.set_property('sensitive', self.__ftp_engine != None and self.__ftp_engine.is_idle and self.__selected_count == 1)
        return

    def on_transfer_action_execute(self, obj, *args):
        self.on_queue_action_execute(obj, *args)
        self.__main_window.transfer_view.start_transfer(obj, None)
        return

    def on_transfer_as_action_execute(self, obj, *args):
        def on_dialog_response(dlg, resp, *args):
            v = utf8_to_unicode(string.strip(dlg.value))
            dlg.destroy()
            if resp == RESPONSE_OK and v != '':
                self.__list.get_selection().selected_foreach(
                    self.add_to_queue, v)
                self.__main_window.transfer_view.start_transfer(obj, None)
            return
        
        if len(self.__selections) != 1: return
        iter = self.__selections[0]
        info = self.__model.get_value(iter, RemoteView.COL_INFO)
        d = GeneralInputDialog(_("Transfer As"), _("Name"), info.name)
        d.connect('response', on_dialog_response)
        resp = d.run()
        return

    def on_queue_action_execute(self, obj, *args):
        self.__list.get_selection().selected_foreach(self.add_to_queue, None)
        return

    def on_queue_as_action_execute(self, obj, *args):
        def on_dialog_response(dlg, resp, *args):
            v = utf8_to_unicode(string.strip(dlg.value))
            dlg.destroy()
            if resp == RESPONSE_OK and v != '':
                self.__list.get_selection().selected_foreach(
                    self.add_to_queue, v)
            return
        if len(self.__selections) != 1: return
        iter = self.__selections[0]
        info = self.__model.get_value(iter, RemoteView.COL_INFO)
        d = GeneralInputDialog(_("Queue As"), _("Name"), info.name)
        d.connect('response', on_dialog_response)
        resp = d.run()
        return
    
    def on_delete_action_update(self, obj):
        obj.set_property(
            'sensitive',
            self.__ftp_engine != None and self.__ftp_engine.is_idle \
            and self.__selected_count > 0)
        return

    def on_delete_action_execute(self, obj, *args):
        def on_dialog_response(dlg, resp, *args):
            dlg.destroy()
            if resp == RESPONSE_YES:
                gobject.idle_add(self.ftp_delete_selected)
            return
        
        dlg = MessageDialog(flags=DIALOG_MODAL,
                            type=MESSAGE_QUESTION,
                            buttons=BUTTONS_YES_NO,
                            message_format=_("Delete selected object(s), are you sure?"))
        dlg.connect('response', on_dialog_response)
        dlg.show()
        dlg.set_modal(True)
        return

    def on_move_action_update(self, obj):
        obj.set_property(
            'sensitive', self.__ftp_engine != None and \
            self.__ftp_engine.is_idle and self.__selected_count > 0)
        return

    def on_move_action_execute(self, obj, *args):
        def on_dialog_response(dlg, resp, *args):
            v = utf8_to_unicode(string.strip(dlg.value))
            d.destroy()
            if resp == RESPONSE_OK and v != '':
                if v[-1] != '/':
                    v = v + '/'
                if v != self.current_path + '/':
                    self.__list.get_selection().selected_foreach(
                        self.ftp_move_each, v)
                self.ftp_refresh(1)
            return
            
        d = GeneralInputDialog(_("Move Selected Object(s)?"), _("To"),
                               u'%s/' % self.current_path)
        d.connect('response', on_dialog_response)
        resp = d.run()
        return

    def on_rename_action_update(self, obj):
        obj.set_property('sensitive', self.__ftp_engine != None and self.__ftp_engine.is_idle and self.__selected_count == 1)
        return

    def on_rename_action_execute(self, obj, *args):
        def on_dialog_response(dlg, resp, *args):
            v = utf8_to_unicode(string.strip(dlg.value))
            dlg.destroy()
            if resp == RESPONSE_OK and v != '':
                # rename the file(mv)
                self.ftp_rename(info.name, v)
                self.ftp_refresh(1)
            return
            
        if self.__selected_count != 1: return
        # show a input dialog
        info = self.__model.get_value(self.__selections[0],
                                      RemoteView.COL_INFO)
        d = GeneralInputDialog(_("Rename file"),
                               _("Please input new name of the file:"),
                               info.name)
        d.connect('response', on_dialog_response)
        resp = d.run()
        return

    def on_copyurl_action_update(self, obj):
        obj.set_property('sensitive', self.__ftp_engine != None and self.__ftp_engine.is_idle and self.selected_count > 0)
        return

    def on_copyurl_action_execute(self, obj, *args):
        if self.__clipboard == None:
            display = gtk.gdk.display_get_default()
            self.__clipboard = Clipboard(display, 'CLIPBOARD')
        info = self.__model.get_value(self.__selections[0],
                                      RemoteView.COL_INFO)
        url = self.current_url
        if url[-1] != '/':
            url = url + '/' + info.name
        else:
            url = url + info.name
        if info.isdir():
            url = url + '/'
        self.__clipboard.set_text(unicode_to_utf8(url))
        return

    def on_mkdir_action_update(self, obj):
        obj.set_property('sensitive', self.__ftp_engine != None and self.__ftp_engine.is_idle)
        return

    def on_mkdir_action_execute(self, obj, *args):
        def on_dialog_response(dlg, resp, *args):
            v = utf8_to_unicode(string.strip(dlg.value))
            dlg.destroy()
            if resp == RESPONSE_OK and v != '':
                self.ftp_mkdir(v)
                self.ftp_refresh(1)
            return
    
        # show a input dialog
        d = GeneralInputDialog(_("New Folder"), _("Name of Folder"), '')
        d.connect('response', on_dialog_response)
        d.run()
        return

    def on_chdir_action_update(self, obj):
        obj.set_property('sensitive', self.__ftp_engine != None and self.__ftp_engine.is_idle)
        return

    def on_chdir_action_execute(self, obj, *args):
        def on_dialog_response(dlg, resp, *args):
            v = utf8_to_unicode(string.strip(dlg.value))
            dlg.destroy()
            if resp == RESPONSE_OK and v != '':
                if value[-1] != '/':
                    v = v + '/'
                if value != self.current_path + '/':
                    self.ftp_cd_path(v)
            return
            
        d = GeneralInputDialog(_("Change Folder"), _("Name of Folder"),
                               self.current_path + '/')
        d.connect('response', on_dialog_response)
        d.run()
        return

    def on_updir_action_update(self, obj):
        obj.set_property('sensitive', self.__ftp_engine != None and self.__ftp_engine.is_idle)
        return

    def on_updir_action_execute(self, obj, *args):
        self.ftp_cd_path('..')

    def ftp_open(self):

        def on_open_finished(*args):
            logging.debug('on_open_finished runs in %s' \
                          % self.main_window.coralftp.get_thread_name())
            # be CONNECTING or NONE
            if self.__ftp_engine.status == STATUS_IDLE:
                # display site name in caption
                display_site_name_in_caption = config_value(
                    self.__config, 'display', 'display_site_name_in_caption')
                if display_site_name_in_caption:
                    self.main_window.widget.set_title(
                        'CoralFTP: %s' % self.__site_info['site_name'])
                else:
                    self.main_window.widget.set_title('CoralFTP')
                
                if self.__ftp_engine.welcome_message != None:
                    dlg_xml = get_glade_xml('welcome_message_dialog')
                    dlg = dlg_xml.get_widget('welcome_message_dialog')
                    textview = dlg_xml.get_widget('tv_welcome_message')
                    buffer = textview.get_buffer()
                    buffer.set_text(self.__ftp_engine.welcome_message)
                    dlg.connect('response', lambda obj, *args: obj.destroy())
                    dlg.show()
                    dlg.set_modal(True)
                    
                remote_path = self.__site_info['remote_path']
                if remote_path and remote_path != '':
                    self.ftp_cd_path(remote_path)
                else:
                    self.ftp_refresh()
                local_path = self.__site_info['local_path']
                if local_path and local_path != '':
                    self.__main_window.local_view.set_path(local_path)
            return
        # end of on_open_finished

        # start of ftp_open
        if self.__ftp_engine and self.__ftp_engine.status != STATUS_NONE:
            self.ftp_close()
            self.__ftp_close_event.wait()
            
        if self.__ftp_engine == None:
            self.__ftp_engine = FTPEngine(self)
            self.__ftp_engine.connect('quit', self.on_ftp_engine_quit)
            self.__ftp_engine.connect(
                'idle', lambda *args: gobject.idle_add(self.on_ftp_idle, *args))
            self.__ftp_engine.connect(
                'delay', lambda *args: gobject.idle_add(self.on_ftp_delay,
                                                        *args))
            self.__cd_succeeded_hids.append(self.__ftp_engine.connect(
                'cd-succeeded', self.ftp_refresh))
            self.__ftp_engine.connect(
                'put-succeeded',
                self.__main_window.transfer_view.on_transfer_succeeded)
            self.__ftp_engine.connect(
                'put-failed',
                self.__main_window.transfer_view.on_transfer_failed)
            self.__ftp_engine.connect(
                'get-succeeded',
                self.__main_window.transfer_view.on_transfer_succeeded)
            self.__ftp_engine.connect(
                'get-failed',
                self.__main_window.transfer_view.on_transfer_failed)
            self.__ftp_engine.connect(
                'log-message', self.__main_window.log_view)
            self.__ftp_engine.start()
            self.__ftp_engine.is_idle_event.wait()

            # if remote path specified, cd into it after open succeeded.
            self.__ftp_engine.connect(
                'open-succeeded', on_open_finished)
            self.__ftp_engine.connect(
                'open-failed', on_open_finished)

        self.__ftp_engine.open(self.__site_info)
        return
        
    def ftp_close(self):
        self.__ftp_close_event.clear()
        if self.__ftp_engine:
            self.__ftp_engine.quit()
        return

    def ftp_refresh(self, clear_cache=False):
        self.__model.clear()
        self.folder_count = 0
        self.file_count = 0
        self.__selected_count = 0
        self.__selections = []
        self.__ftp_engine.pwd(PwdUpdater(self))
        self.__ftp_engine.list(updater=ListUpdater(self),
                               clear_cache=clear_cache)

    def ftp_delete_selected(self):
        def on_delete_succeeded(obj, hids, names):
            if len(names) > 0:
                isdir, name = names.pop()
                gobject.idle_add(self.__ftp_engine.delete, name, isdir)
                return False
            if len(names) == 0:
                while len(hids) > 0: self.__ftp_engine.disconnect(hids.pop())
                gobject.idle_add(self.ftp_refresh, True)
                return True
        def on_delete_failed(obj, errno, errmsg, hids, names):
            if errno != '550' and len(names) > 0: # Permission denied
                isdir, name = names.pop()
                gobject.idle_add(self.__ftp_engine.delete, name, isdir)
                return False
            if len(names) == 0:
                while len(hids) > 0: self.__ftp_engine.disconnect(hids.pop())
                gobject.idle_add(self.ftp_refresh, True)
                return True
        def on_cci_finish(names):
            hids = []
            hids.append(self.__ftp_engine.connect('delete-succeeded',
                                                  on_delete_succeeded,
                                                  hids, names))
            hids.append(self.__ftp_engine.connect('delete-failed',
                                                  on_delete_failed,
                                                  hids, names))
            if len(names) > 0:
                isdir, name = names.pop()
                self.__ftp_engine.delete(name, isdir)
            return
            
        dirs = []
        names = []
        for iter in self.__selections:
            name = utf8_to_unicode(self.__model.get_value(iter, self.COL_NAME))
            isdir = self.__model.get_value(iter, self.COL_ATTR)[0] == 'd'
            if isdir:
                dirs.append(name)
            else:
                names.append((False, u'%s/%s' % (self.current_path, name)))
        self.ftp_collect_children_information(dirs, names, on_cci_finish)
        return False

    def ftp_collect_children_information(self, dirs, names, func,\
                                         clear_cache=False):
        class ChildCollectUpdater:
            items = []
            def __call__(self, filelist):
                if len(filelist) > 0: self.items.extend(filelist)
                return False
            
        def on_leave_success(obj, *args):
            while len(hids) > 0: self.__ftp_engine.disconnect(hids.pop())
            next()
            return

        def on_leave_failed(obj, *args):
            while len(hids) > 0: self.__ftp_engine.disconnect(hids.pop())
            next()
            return
 
        def on_list_success(obj, dirname, *args):
            while len(hids) > 0: self.__ftp_engine.disconnect(hids.pop())
            while len(collection.items) > 0:
                attr, childnum, owner, group, size, month, day, year_or_day, name \
                          = collection.items.pop()
                if name in ('.', '..'): continue
                isdir = attr[0] == 'd'
                if isdir:
                    dirs.append(dirname + '/' + name)
                    logging.debug('add directory %s / %s' % (dirname, name))
                else:
                    names.append((False, '%s/%s' % (dirname, name)))
                    logging.debug('add file %s / %s' % (dirname, name))
            hid = self.__ftp_engine.connect('cd-succeeded', on_leave_success)
            hids.append(hid)
            hid = self.__ftp_engine.connect('cd-failed', on_leave_failed)
            hids.append(hid)
            self.__ftp_engine.cd(self.current_path)
            return

        def on_list_failed(obj, *args):
            while len(hids) > 0: self.__ftp_engine.disconnect(hids.pop())
            names.pop()
            next()
            return

        def on_enter_success(obj, dirname, *args):
            while len(hids) > 0: self.__ftp_engine.disconnect(hids.pop())
            hid = self.__ftp_engine.connect('list-succeeded', on_list_success, dirname, collection)
            hids.append(hid)
            hid = self.__ftp_engine.connect('list-failed', on_list_failed)
            hids.append(hid)
            self.__ftp_engine.list(updater=collection, clear_cache=clear_cache)
            return

        def on_enter_failed(obj, *args):
            while len(hids) > 0: self.__ftp_engine.disconnect(hids.pop())
            names.pop()
            next()
            return

        def next():
            if len(dirs) > 0:
                dirname = dirs.pop()
                names.append((True, dirname))
                hid = self.__ftp_engine.connect('cd-succeeded', on_enter_success, dirname)
                hids.append(hid)
                hid = self.__ftp_engine.connect('cd-failed', on_enter_failed)
                hids.append(hid)
                self.__ftp_engine.cd(dirname)
            else:
                finish()
            return

        def start():
            for hid in self.__cd_succeeded_hids:
                self.__ftp_engine.handler_block(hid)
            next()
            return

        def finish():
            for hid in self.__cd_succeeded_hids:
                self.__ftp_engine.handler_unblock(hid)
            func(names)
            return
            
        hids = []
        collection = ChildCollectUpdater()
        start()
        return

    def ftp_move_each(self, treemodel, path, iter, data):
        name = treemodel.get_value(iter, RemoteView.COL_NAME)
        self.__ftp_engine.move(utf8_to_unicode(name),
                               utf8_to_unicode('%s%s' % (data, name)))
        return

    def ftp_rename(self, old_name, new_name):
        self.__ftp_engine.move(utf8_to_unicode(old_name),
                               utf8_to_unicode(new_name))
        return

    def ftp_mkdir(self, name):
        self.__ftp_engine.mkdir(utf8_to_unicode(name))
        return

    def on_ftp_engine_quit(self, obj, *args):
        logging.debug('remote_view.on_ftp_engine_quit begin')
        self.__ftp_engine = None
        self.__model.clear()
        self.__ftp_close_event.set()
        logging.debug('remote_view.on_ftp_engine_quit finish')
        gobject.idle_add(logging.debug, 'last idle_add messag')
        return

    def __count_selection(self, tree, path, iter, data):
        info = self.__model.get_value(iter, RemoteView.COL_INFO)
        if info.name != '..':
            data.append(iter)
        return

    def __select_func(self, *args):
        return self.__change_selection_now

    def on_list_button_release(self, obj, event):
        # count selection
        selection = self.__list.get_selection()
        self.__selections = []
        selection.selected_foreach(self.__count_selection, self.__selections)
        self.__selected_count = len(self.__selections)
        self.update_status1()

        if event.button == 3:
            self.__menu.popup(None, None, None, 0, 0)
        return
    
    def on_list_button_press(self, obj, event):
        if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1:
            self.__change_selection_now = True
        if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
            self.__change_selection_now = False
            
        self.__main_window.set_active_view(self)

        # count selection
        selection = self.__list.get_selection()
        self.__selections = []
        selection.selected_foreach(self.__count_selection, self.__selections)
        self.__selected_count = len(self.__selections)
        self.update_status1()
                
        if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1:
            if selection.count_selected_rows() == 1 and self.__selected_count == 0:
                self.ftp_cd_path('..')
                
            if self.__selected_count == 1:
                info = self.__model.get_value(self.__selections[0],
                                              RemoteView.COL_INFO)
                if info.isdir():
                    self.ftp_cd_path(info.name)
                else:
                    # transfer file
                    pass
        return

    def set_active_view(self, active=False):
        if active:
            self.active_indicator.set_name('active_view_indicator')
        else:
            self.active_indicator.set_name('')

    def ftp_cd_path(self, new_path):
        if not isinstance(new_path, unicode):
            new_path = utf8_to_unicode(new_path)
        self.__ftp_engine.cd(new_path)
        return

    def on_ftp_idle(self, obj, t):
        if not self.__show_idle_time or t < 0:
            s = ''
        else:
            s = 'Idle: %d' % t
        if self == self.__main_window.right_view:
            self.__main_window.status2.set_text(s)
        else:
            self.__main_widnow.status1.set_text(s)
        return

    def on_ftp_delay(self, obj, t):
        if not self.__show_idle_time or t == 0:
            s = ''
        else:
            s = 'Delay: %d' % t
        if self == self.__main_window.right_view:
            self.__main_window.status2.set_text(s)
        else:
            self.__main_widnow.status1.set_text(s)
        return

    def filelist_sort(self, treemodel, iter1, iter2, *arg):
        sort_folder_on_top = config_value(self.__config, 'display',
                                          'sort_folder_on_top')
        col, order = treemodel.get_sort_column_id()
        if order == SORT_ASCENDING:
            parent_weight = 1
            if sort_folder_on_top:
                dir_weight = 2
            else:
                dir_weight = 3
            file_weight = 3
        else:
            parent_weight = 3
            if sort_folder_on_top:
                dir_weight = 2
            else:
                dir_weight = 1
            file_weight = 1

        item1 = treemodel.get_value(iter1, self.COL_INFO)
        item2 = treemodel.get_value(iter2, self.COL_INFO)

        if col == RemoteView.COL_NAME:
            weight1 = item1.name
        elif col == RemoteView.COL_SIZE:
            weight1 = item1.size
        elif col == RemoteView.COL_TYPE:
            weight1 = treemodel.get_value(iter1, RemoteView.COL_TYPE)
        elif col == RemoteView.COL_OWNER:
            weight1 = item1.owner
        elif col == RemoteView.COL_GROUP:
            weight1 = item1.group
        elif col == RemoteView.COL_DATE:
            weight1 = item1.date
        if item1.name == '..':
            weight1 = (parent_weight, weight1)
        elif item1.isdir():
            weight1 = (dir_weight, weight1)
        else:
            weight1 = (file_weight, weight1)

        if col == RemoteView.COL_NAME:
            weight2 = item2.name
        elif col == RemoteView.COL_SIZE:
            weight2 = item2.size
        elif col == RemoteView.COL_TYPE:
            weight2 = treemodel.get_value(iter2, RemoteView.COL_TYPE)
        elif col == RemoteView.COL_OWNER:
            weight2 = item2.owner
        elif col == RemoteView.COL_GROUP:
            weight2 = item2.group
        elif col == RemoteView.COL_DATE:
            weight2 = item2.date
        if item2.name == '..':
            weight2 = (parent_weight, weight2)
        elif item2.isdir():
            weight2 = (dir_weight, weight2)
        else:
            weight2 = (file_weight, weight2)
        if weight1 > weight2:
            return 1
        elif weight1 < weight2:
            return -1
        else:
            return 0
        
    def add_to_queue(self, treemodel, path, iter, data):
        if self.__main_window.mode == self.__main_window.MODE_FTP:
            info = treemodel.get_value(iter, RemoteView.COL_INFO)
            direction = TransferView.DIRECTION_DOWN
            source = 'ftp://%s@%s%s/%s' % (self.site_info['username'],
                                           self.site_info['server_addr'],
                                           self.current_path,
                                           info.name)
            local_view = self.__main_window.local_view
            if data == None:
                target = '%s/%s' % (local_view.current_path, info.name)
            else:
                target = '%s/%s' % (local_view.current_path, data)
            if info.isdir():
                source = source + '/'
                target = target + '/'
            size = info.size
            self.__main_window.transfer_view.add_to_queue(
                direction, source, target, size, self.__site_info, None)
        return
    
    def add_all_to_queue(self, trans_iter, target_path):
        """
        Add all the item in the list to the transfer queue.
        """
        tview = self.__main_window.transfer_view
        direction = TransferView.DIRECTION_DOWN
        iter = self.__model.get_iter_first()
        while iter:
            info = self.__model.get_value(iter, RemoteView.COL_INFO)
            if info.name != '..':
                source = '%s/%s' % (self.current_path, info.name)
                target = '%s%s' % (target_path, info.name)
                if info.isdir():
                    source = source + '/'
                    target = target + '/'
                trans_iter = tview.add_to_queue(
                    direction, source, target, info.size,
                    self.__site_info, None, trans_iter)
            iter = self.__model.iter_next(iter)
        return

    def on_display_file_size_as_change(self, config, cnxn_id, value, *args):
        if not value:
            return
        else:
            iter = self.__model.get_iter_first()
            while iter:
                info = self.__model.get_value(iter, RemoteView.COL_INFO)
                size = info.size
                self.__model.set_value(iter, RemoteView.COL_SIZE,
                                       format_size(value.to_string(), size))
                iter = self.__model.iter_next(iter)
        return

    def on_file_list_window_font_change(self, config, cnxn_id, value, *args):
        if not value:
            return
        return

    def on_remote_dir_entry_activate(self, obj, *args):
        new_path = self.__cmb_dir.child.get_text()
        self.ftp_cd_path(new_path)
        return

    def on_sort_action_execute(self, action, widget, *args):
        if self.__change_sort_menu_item:
            self.__change_sort_menu_item = False

            column, order = self.__model.get_sort_column_id()
            widget_name = widget.get_name()
            if widget_name == 'mi_remote_arr_by_name':
                column = RemoteView.COL_NAME
            elif widget_name == 'mi_remote_arr_by_file_extension':
                column = RemoteView.COL_TYPE
            elif widget_name == 'mi_remote_arr_by_size':
                column = RemoteView.COL_SIZE
            elif widget_name == 'mi_remote_arr_by_date':
                column = RemoteView.COL_DATE
            elif widget_name == 'mi_remote_arr_by_owner':
                column = RemoteView.COL_OWNER
            elif widget_name == 'mi_remote_arr_by_group':
                column = RemoteView.COL_GROUP
            elif widget_name == 'mi_remote_arr_ascending':
                order = SORT_ASCENDING
            elif widget_name == 'mi_remote_arr_descending':
                order = SORT_DESCENDING
            else:
                raise ValueError, widget_name

            self.__model.set_sort_column_id(column, order)
            
            self.__change_sort_menu_item = True
        return

    def on_list_sort_column_changed(self, obj, *args):
        if self.__change_sort_menu_item:
            self.__change_sort_menu_item = False
            
            column, order = obj.get_sort_column_id()
            if column == RemoteView.COL_NAME:
                widget_name = 'mi_remote_arr_by_name'
            elif column == RemoteView.COL_TYPE:
                widget_name = 'mi_remote_arr_by_file_extension'
            elif column == RemoteView.COL_SIZE:
                widget_name = 'mi_remote_arr_by_size'
            elif column == RemoteView.COL_DATE:
                widget_name = 'mi_remote_arr_by_date'
            elif column == RemoteView.COL_OWNER:
                widget_name = 'mi_remote_arr_by_owner'
            elif column == RemoteView.COL_GROUP:
                widget_name = 'mi_remote_arr_by_group'
            widget = self.__menu_xml.get_widget(widget_name)
            widget.set_active(True)
            if order == SORT_ASCENDING:
                widget_name = 'mi_remote_arr_ascending'
            elif order == SORT_DESCENDING:
                widget_name = 'mi_remote_arr_descending'
            widget = self.__menu_xml.get_widget(widget_name)
            widget.set_active(True)
            
            self.__change_sort_menu_item = True            
        return

    def on_auto_size_file_list_columns_change(self, config, id, value, *args):
        for column in self.__list.get_columns():
            if value.get_bool():
                column.set_sizing(TREE_VIEW_COLUMN_AUTOSIZE)
            else:
                column.set_sizing(TREE_VIEW_COLUMN_GROW_ONLY)
        return 

    def on_show_site_idle_time_change(self, config, id, value, *args):
        self.__show_idle_time = value.get_bool()
        return

    def on_use_custom_file_listing_font_change(self, config, id, value, *args):
        self.__use_custom_file_listing_font = value.get_bool()
        self.__change_font()
        return

    def on_file_listing_font_change(self, config, id, value, *args):
        self.__file_listing_font = value.get_string()
        self.__change_font()
        return

    def __change_font(self):
        for renderer in self.__text_renderers:
            if self.__use_custom_file_listing_font:
                renderer.set_property('font', self.__file_listing_font)
            else:
                renderer.set_property('font', None)
        return

    def on_show_hidden_action_execute(self, *args):
        self.__show_hidden = not self.__show_hidden
        if self.__ftp_engine != None and self.__ftp_engine.is_idle:
            self.ftp_refresh()
        return

class RemoteFileInfo:
    name = None
    attr = None
    size = None
    child_num = None
    owner = None
    group = None
    date = None

    def isdir(self):
        return self.attr[0] == 'd'
    
class PwdUpdater:
    def __init__(self, rview):
        self.__rview = rview
        return

    def __call__(self, new_path):
        self.__new_path = new_path
        (scheme, location, path, query, fragment) = urlsplit(self.__new_path)
        path = string.replace(unquote(path), '//', '/')
        self.__rview.cmb_dir.child.set_text(path)
        self.__rview.status2.set_text(self.__rview.current_url)
        return

class ListUpdater:
    def __init__(self, rview):
        self.__rview = rview
        self.__model = rview.model
        self.__time_reg = re.compile('([A-Za-z]{3}) (\d{1,2}) (\d{1,2}):(\d{1,2})')
        self.__date_reg = re.compile('([A-Za-z]{3}) (\d{1,2}) (\d{4})')
        return

    def __call__(self, filelist):
        def update(filelist):
            display_file_size_as = config_value(
                self.__rview.config, 'display', 'display_file_size_as')

            for attr, childnum, owner, group, size, month, day, year_or_day, name in filelist:
                if name == '.': continue
                if name[0] == '.' and not self.__rview.show_hidden: continue
                iter = self.__model.append()
                
                info = RemoteFileInfo()
                info.name = name
                info.attr = attr
                info.childnum = string.atoi(childnum)
                info.owner = owner
                info.group = group
                info.size = string.atoi(size)
                date = '%s %s %s' % (month, day, year_or_day)
                if len(year_or_day) == 5:
                    t = self.__time_reg.match(date)
                    if t != None:
                        t = t.groups()
                        t = datetime.datetime(
                            time.localtime()[0], get_month_index(t[0]),
                            string.atoi(t[1]), string.atoi(t[2]),
                            string.atoi(t[3])).utctimetuple()
                        if t > datetime.datetime.utcnow().utctimetuple():
                            t = datetime.datetime(t[0] - 1, *t[1:4]).utctimetuple()
                else:
                    t = self.__date_reg.match(date)
                    if t != None:
                        t = t.groups()
                        t = datetime.datetime(
                            string.atoi(t[2]), get_month_index(t[0]),
                            string.atoi(t[1])).utctimetuple()
                info.date = t
                self.__model.set_value(iter, RemoteView.COL_INFO, info)
                self.__model.set_value(iter, RemoteView.COL_ATTR, attr)
                self.__model.set_value(iter, RemoteView.COL_OWNER, owner)
                self.__model.set_value(iter, RemoteView.COL_GROUP, group)
                self.__model.set_value(iter, RemoteView.COL_SIZE,
                                       format_size(display_file_size_as,
                                                   string.atoi(size)))
                self.__model.set_value(iter, RemoteView.COL_DATE, date)
                if name == '..':
                    self.__model.set_value(iter, RemoteView.COL_NAME,
                                           _("Parent Directory"))
                else:
                    self.__model.set_value(iter, RemoteView.COL_NAME, name)
                if attr[0] == 'd':
                    if name == '..':
                        image = gdk.pixbuf_new_from_file(
                            os.path.join(coralftp_datadir, 'parent_dir.png'))
                    else:
                        image = gdk.pixbuf_new_from_file(
                            os.path.join(coralftp_datadir, 'folder.png'))
                        self.__rview.folder_count = self.__rview.folder_count + 1
                    self.__model.set_value(iter, RemoteView.COL_ICON, image)
                    filetype = _("Folder")
                else:
                    pos = string.rfind(name, '.')
                    if pos > 0:
                        filetype = name[pos:]
                    else:
                        filetype = ''
                    self.__rview.file_count = self.__rview.file_count + 1
                self.__model.set_value(iter, RemoteView.COL_TYPE, filetype)
                self.__rview.update_status1()
            return False
        
        gobject.idle_add(update, filelist)
        return False
